package pers.yefeng.thread;

import pers.yefeng.constant.StreamConstant;
import pers.yefeng.vo.StreamParamV0;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * @date 2021/05/24 14:45:38
 */
@Slf4j
public abstract class MediaThread extends Thread {

    static {
        avutil.av_log_set_level(avutil.AV_LOG_ERROR);
        FFmpegLogCallback.set();
    }

    public MediaThread(StreamParamV0 streamParamV0) {
        this.sourceAddress = streamParamV0.getSourceAddress();
        this.cameraAddress = this.sourceAddress.substring(this.sourceAddress.indexOf("@") + 1);


        if (StringUtils.isNotBlank(streamParamV0.getVideoType())) {
            this.videoType = streamParamV0.getVideoType();
        }
        if (StringUtils.isNotBlank(streamParamV0.getConversionType())) {
            this.conversionType = streamParamV0.getConversionType();
        }
    }


    /**
     * 源视屏地址
     */
    protected final String sourceAddress;


    /**
     * 摄像头地址
     */
    @Getter
    protected String cameraAddress;

    /**
     * 播放地址
     */
    @Getter
    protected String playAddress;
    /**
     * 视频类型：flv、hls。默认flv
     */
    @Getter
    protected String videoType = StreamConstant.VIDEO_TYPE_FLV;

    /**
     * 流转换类型 自动（auto），转码（transcoding），转复用（reuse）。默认自动
     */
    @Getter
    protected String conversionType = StreamConstant.CONVERSION_TYPE_AUTO;


    /**
     * 状态
     */
    protected boolean running = false;
    @Getter
    protected boolean grabberStatus = false;
    protected boolean recorderStatus = false;


    /**
     * 是否支持转复用
     */
    protected boolean transferFlag = false;

    /**
     * 拉流器
     */
    protected FFmpegFrameGrabber grabber;
    /**
     * 推流录制器
     */
    protected FFmpegFrameRecorder recorder;


    /**
     * 记录各个连接信息
     */
    @Getter
    protected ConcurrentHashMap<String, Long> httpMap = new ConcurrentHashMap<>();

    /**
     * 没有客户链接的开始时间
     */
    protected long notHasClientStartTime = 0;

    /**
     * 记录服务开启进度 1：进行中，2：开启成功，3：开启失败
     */
    @Getter
    protected int startProgress = StreamConstant.SERVICE_START_PROGRESS_PROCESSING;
    /**
     * 开启进度msg
     */
    @Getter
    protected String startProgressMsg;


    /**
     * 上次服务重启时间
     */
    protected long lastRestartTime = 0;

    /**
     * 重新建立连接
     */
    protected boolean reConnection = false;


    /**
     * 重启服务
     */
    protected boolean restartService = false;


    /**
     * 标记服务正在启动中
     */
    protected boolean starting = true;


    /**
     * 创建拉流器
     *
     * @return boolean
     */
    public MediaThread createGrabber() {
        log.info("{} | {} | {} | 创建拉流器……", sourceAddress, videoType, conversionType);
        // 拉流器
        grabber = new FFmpegFrameGrabber(sourceAddress);
        // 超时时间(5秒)
        grabber.setOption("stimoout", "5000000");
        grabber.setOption("threads", "1");
        grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        // 设置缓存大小，提高画质、减少卡顿花屏
        grabber.setOption("buffer_size", "1024000");
//        grabber.setOption("buffer_size", "100");
        // 如果为rtsp流，增加配置
        if ("rtsp".equals(sourceAddress.substring(0, 4))) {
            // 设置打开协议tcp / udp
            grabber.setOption("rtsp_transport", "tcp");
            //首选TCP进行RTP传输
            grabber.setOption("rtsp_flags", "prefer_tcp");
            //设置超时时间
            // -stimeout的单位是us 微秒(1秒=1*1000*1000微秒)。
            grabber.setOption("stimeout", "5*1000*1000");
        }
        try {
            grabber.start();
            grabberStatus = true;
        } catch (FrameGrabber.Exception e) {
            log.error("{} | 创建拉流器异常！", sourceAddress, e);
            setProgress(StreamConstant.SERVICE_START_PROGRESS_FAILURE, "创建拉流器失败！");
        }
        return this;
    }

    /**
     * 关闭拉流器
     */
    public void closeGrabber() {
        try {
            log.info("{} | 关闭拉流器……", sourceAddress);
            if (ObjectUtil.isNotNull(grabber)) {
                grabber.close();
            }

        } catch (IOException e) {
            log.error("{} | 关闭拉流器异常", sourceAddress, e);
        }
    }

    /**
     * 创建转码推流录制器
     *
     * @return b
     */
    protected abstract MediaThread createRecodeRecorder();

    /**
     * 是否支持转复用
     *
     * @return boolean
     */
    protected boolean supportReuse() {
        if (StreamConstant.CONVERSION_TYPE_AUTO.equals(conversionType) || StreamConstant.CONVERSION_TYPE_REUSE.equals(conversionType)) {
            int vcodec = grabber.getVideoCodec();
            int acodec = grabber.getAudioCodec();
//        return (avcodec.AV_CODEC_ID_H264 == vcodec || avcodec.AV_CODEC_ID_H263 == vcodec) && (avcodec.AV_CODEC_ID_AAC == acodec || avcodec.AV_CODEC_ID_AAC_LATM == acodec);
            return (avcodec.AV_CODEC_ID_H264 == vcodec) && (avcodec.AV_CODEC_ID_AAC == acodec);
        } else {
            return false;
        }
    }

    /**
     * 开启转流服务
     *
     * @return view
     */
    protected abstract void transform();

    /**
     * 停止转流服务
     */
    public void stopTransform() {
        this.running = false;
        this.restartService = false;
    }


    public abstract void addFlvHttpClient(HttpServletRequest request, HttpServletResponse response);

    /**
     * hls 获取视频流
     *
     * @param request  request
     * @param response response
     * @param playName 文件名称
     */
    public abstract void getHlsFile(HttpServletRequest request, HttpServletResponse response, String playName);

    /**
     * 设置服务开启进度
     *
     * @param process 进度
     * @param msg     消息
     */
    protected void setProgress(int process, String msg) {
        this.startProgress = process;
        this.startProgressMsg = msg;
    }


    /**
     * 判断有没有客户端链接，如果没有，则关闭服务
     */
    public void hasClient() {
        if (CollectionUtil.isNotEmpty(httpMap)) {
            this.notHasClientStartTime = 0;
            return;
        }
        // 开始时间
        if (this.notHasClientStartTime == 0) {
            this.notHasClientStartTime = System.currentTimeMillis();
        }
        // 如果三分钟之内没有有效链接，则关闭转流服务
        if ((System.currentTimeMillis() - notHasClientStartTime) > (5 * 60 * 1000L)) {
            log.error("{} | 长时间无链接，关闭服务！", sourceAddress);
            this.running = false;
        }


    }

    @Override
    public void run() {
        this.createGrabber().createRecodeRecorder().transform();
    }

    /**
     * 记录hls片段
     *
     * @param request
     * @param playAddress
     * @param playName
     */
    public abstract void recordHls(HttpServletRequest request, String playAddress, String playName);


}